package com.luoyx.vjsb.activiti.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.luoyx.vjsb.activiti.entity.ProcessDefinition;
import com.luoyx.vjsb.activiti.service.IProcessDefinitionService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipInputStream;

/**
 * <p>
 * 流程定义
 * </p>
 *
 * @author luoyuanxiang <p>luoyuanxiang.github.io</p>
 * @since 2020/5/15 10:46
 */
@Service
public class ProcessDefinitionServiceImpl implements IProcessDefinitionService {

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private RuntimeService runtimeService;

    /**
     * 获取分页流程定义
     *
     * @param processDefinition 1
     * @return 1
     */
    @Override
    public Page<ProcessDefinition> listProcessDefinition(ProcessDefinition processDefinition) {
        Page<ProcessDefinition> processDefinitionPage = new Page<>(processDefinition.getCurrent(), processDefinition.getSize());
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        processDefinitionQuery.orderByProcessDefinitionId().orderByProcessDefinitionVersion().desc();
        if (StrUtil.isNotBlank(processDefinition.getName())) {
            processDefinitionQuery.processDefinitionNameLike("%" + processDefinition.getName() + "%");
        }
        if (StrUtil.isNotBlank(processDefinition.getKey())) {
            processDefinitionQuery.processDefinitionKeyLike("%" + processDefinition.getKey() + "%");
        }
        if (StrUtil.isNotBlank(processDefinition.getCategory())) {
            processDefinitionQuery.processDefinitionCategoryLike("%" + processDefinition.getCategory() + "%");
        }

        List<org.activiti.engine.repository.ProcessDefinition> processDefinitionList;
        processDefinitionList = processDefinitionQuery.listPage((int)((processDefinition.getCurrent() - 1) * processDefinition.getSize()),
            (int)processDefinition.getSize());
        List<ProcessDefinition> processDefinitions = new ArrayList<>(processDefinitionList.size());
        for (org.activiti.engine.repository.ProcessDefinition definition: processDefinitionList) {
            ProcessDefinitionEntityImpl entityImpl = (ProcessDefinitionEntityImpl) definition;
            ProcessDefinition entity = new ProcessDefinition();
            entity.setId(definition.getId());
            entity.setKey(definition.getKey());
            entity.setName(definition.getName());
            entity.setCategory(definition.getCategory());
            entity.setVersion(definition.getVersion());
            entity.setDescription(definition.getDescription());
            entity.setDeploymentId(definition.getDeploymentId());
            Deployment deployment = repositoryService.createDeploymentQuery()
                .deploymentId(definition.getDeploymentId())
                .singleResult();
            entity.setDeploymentTime(deployment.getDeploymentTime());
            entity.setDiagramResourceName(definition.getDiagramResourceName());
            entity.setResourceName(definition.getResourceName());
            entity.setSuspendState(entityImpl.getSuspensionState() + "");
            if (entityImpl.getSuspensionState() == 1) {
                entity.setSuspendStateName("已激活");
            } else {
                entity.setSuspendStateName("已挂起");
            }
            processDefinitions.add(entity);
        }
        processDefinitionPage.setTotal(processDefinitionQuery.count());
        processDefinitionPage.setRecords(processDefinitions);
        return processDefinitionPage;
    }

    /**
     * 发布流程定义
     *
     * @param filePath 文件位置
     * @throws FileNotFoundException 1
     */
    @Override
    public void deployProcessDefinition(String filePath) throws FileNotFoundException {
        if (StrUtil.isNotBlank(filePath)) {
            if (filePath.endsWith(".zip")) {
                ZipInputStream inputStream = new ZipInputStream(new FileInputStream(filePath));
                repositoryService.createDeployment()
                    .addZipInputStream(inputStream)
                    .deploy();
            } else if (filePath.endsWith(".bpmn")) {
                repositoryService.createDeployment()
                    .addInputStream(filePath, new FileInputStream(filePath))
                    .deploy();
            }
        }
    }

    /**
     * 删除流程定义
     *
     * @param deploymentIds 流程id
     * @return 1
     * @throws Exception 1
     */
    @Override
    public int deleteProcessDeploymentByIds(String[] deploymentIds) throws Exception {
        int counter = 0;
        for (String deploymentId: deploymentIds) {
            List<ProcessInstance> instanceList = runtimeService.createProcessInstanceQuery()
                .deploymentId(deploymentId)
                .list();
            if (!CollectionUtils.isEmpty(instanceList)) {
                // 存在流程实例的流程定义
                throw new Exception("删除失败，存在运行中的流程实例");
            }
            // true 表示级联删除引用，比如 act_ru_execution 数据
            repositoryService.deleteDeployment(deploymentId, true);
            counter ++;
        }
        return counter;
    }

    /**
     * 激活或挂起
     *
     * @param id           流程定义id
     * @param suspendState 1
     */
    @Override
    public void suspendOrActiveApply(String id, Integer suspendState) {
        if (suspendState == 1) {
            // 当流程定义被挂起时，已经发起的该流程定义的流程实例不受影响（如果选择级联挂起则流程实例也会被挂起）。
            // 当流程定义被挂起时，无法发起新的该流程定义的流程实例。
            // 直观变化：act_re_procdef 的 SUSPENSION_STATE_ 为 2
            repositoryService.suspendProcessDefinitionById(id);
        } else if (suspendState == 2) {
            repositoryService.activateProcessDefinitionById(id);
        }
    }
}
